/*
 * Copyright 2010 Thedwick, LLC

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 */
package com.thedwick.jdbcGrabber.analysis.parser;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import Zql.ParseException;
import Zql.ZDelete;
import Zql.ZFromItem;
import Zql.ZInsert;
import Zql.ZQuery;
import Zql.ZSelectItem;
import Zql.ZStatement;
import Zql.ZUpdate;
import Zql.ZqlParser;

/**
 * Uses Zql to parse the sql passed to it.  All references to Zql should be contained in this class
 * and not exposed outside of it.  That way we can swap in a different parser if we want.
 * @author trcull
 *
 */
public class ZqlCapturedStatement implements CapturedStatement {
	private final int count;
	private final String sql;
	private final Set<CapturedTrace> traces;
	private final Set<CapturedTable> tables = new HashSet<CapturedTable>();
	private final Set<CapturedColumn> columns = new HashSet<CapturedColumn>();
	
	public ZqlCapturedStatement(String sql, int count, Set<CapturedTrace> traces) throws IOException {
		this.count = count;
		this.traces = traces;
		this.sql = sql;
		try {
			parseSql(sql);
		} catch (ParseException e) {
			throw new IOException(e);//don't want Zql classes exposed outside this class.
		}
	}

	@SuppressWarnings("deprecation")
	private void parseSql(String sql) throws ParseException {
		if (!sql.endsWith(";")){
			sql = sql + ";"; //Zql requires everything to end with a semi colon
		}
		InputStream input = new java.io.StringBufferInputStream(sql);
		ZqlParser parser = new ZqlParser(input);
		ZStatement stmt = parser.readStatement();
		
		recordTablesAndColumns(stmt);
	}

	private void recordTablesAndColumns(ZStatement stmt) {
		if (stmt instanceof ZQuery){
			recordTablesAndColumns((ZQuery) stmt);
		} else if (stmt instanceof ZDelete) {
			recordTablesAndColumns((ZDelete) stmt);
		} else if (stmt instanceof ZInsert) {
			recordTablesAndColumns((ZInsert) stmt);
		} else if (stmt instanceof ZUpdate) {
			recordTablesAndColumns((ZUpdate) stmt);
		} else {
			throw new UnsupportedOperationException("unrecognized Zql class: " + stmt.getClass());
		}
	}

	@SuppressWarnings("unchecked")
	private void recordTablesAndColumns(ZQuery stmt) {
		Iterator<ZSelectItem> zcolumns = stmt.getSelect().iterator();
		while (zcolumns.hasNext()) {
			ZSelectItem item = zcolumns.next();
			columns.add(new CapturedColumn(item.getColumn()));
		}
		
		Iterator<ZFromItem> ztables = stmt.getFrom().iterator();
		while (ztables.hasNext()) {
			ZFromItem item = ztables.next();
			tables.add(new CapturedTable(item.getTable()));
		}
	}

	private void recordTablesAndColumns(ZDelete stmt) {
		throw new UnsupportedOperationException("unrecognized Zql class: " + stmt.getClass());
	}
	
	private void recordTablesAndColumns(ZInsert stmt) {
		throw new UnsupportedOperationException("unrecognized Zql class: " + stmt.getClass());
	}
	
	private void recordTablesAndColumns(ZUpdate stmt) {
		throw new UnsupportedOperationException("unrecognized Zql class: " + stmt.getClass());
	}
	
	
	@Override
	public Set<CapturedColumn> getColumns() {
		return this.columns;
	}

	@Override
	public int getCount() {
		return this.count;
	}

	@Override
	public Set<CapturedTable> getTables() {
		return this.tables;
	}

	@Override
	public Set<CapturedTrace> getTraces() {
		return this.traces;
	}

	@Override
	public String getSql() {
		return this.sql;
	}

}
